/** \brief Fichier déclarant le mécanisme de sortie.
 *
 * Ce fichier contient la déclaration des objets nécessaires pour une sortie
 * paramétrable.
 *
 * @file FlotMessage.h
 * @author Johan "Solo" Luisier
 * @date Mai 2010
 */
#ifndef FLOTMESSAGE_H
#define FLOTMESSAGE_H 1

#include "CColor.h"

#include <iomanip>
#include <map>
#include <sstream>
#include <string>

/** @brief Décrit les différents niveau de sortie possibles.
 *
 */
enum DebugLevelType
  {
    ALL, /**< Montre tout les messages */
    MEM, /**< Debugging ultra détaillé des fuites de mémoires */
    DEBUG, /**< Message de debugging */
    LFUNC, /**< Annonce de fonctions dans des boucles */
    LINFO, /**< Messages dans des boucles */
    FUNC, /**< Annonce de fonction */
    INFO, /**< Information générale */
    BLANK, /**< Idem, mais sans "    INFO  :" avant le message */
    PROGRESS, /**< - */
    RELEASE, /**< Truc à mettre dans un log même après une release */
    WARN, /**< Qqch est incorrect, fait biip */
    ERR, /**< Désastre, bip aussi */
    OFF /**< Pas d'affichage */
  };

/** @brief Type de manipulation de la sortie.
 *
 * Type personalisé afin de pouvoir définir un opérateur spécialisé dans
 * FlotMessage qui effectuera l'affichage avec un retour à la ligne.
 *
 * Deux valeurs sont définies : soit le message est affiché, suivi d'un retour
 * à la ligne, ou alors on affiche simplement le message (ce qui permet de
 * faire un cin juste derrière).
 *
 * Suggéré par Andre de qt-devnet.
 *
 * @author Johan "Solo" Luisier
 * @date Mai 2010
 */
enum Manipulateur
  {
    FinDeLigne = 0, /**< Correspond à un retour à la ligne simple. */
    AffichageSimple = 1 /**< Correspond à l'affichage sans retour à la
			   ligne. */
  };

/** @brief Objet semblable à @c endl.
 */
static const Manipulateur endmsg( FinDeLigne );

/** @brief Objet forçant l'affichage sans ajouter de retour à la ligne.
 *
 * Cet objet a un comportement semblable à @c flush.
 */
static const Manipulateur endaff( AffichageSimple );

/** @brief %Classe définissant un affichage acceptant des couleurs et des
 * niveaux de sortie.
 *
 * Cette classe permet de réaliser un affichage avec des niveaux de sorties, en
 * affichant le nom de la classe appelante, ainsi que le nom du niveau. De
 * plus, des couleurs peuvent être utilisées pour mieux différencier les
 * niveaux sortie des messages.
 *
 * Inspiré de la classe MsgStream (de LHCb).
 *
 * @author Johan "Solo" Luisier
 */
class FlotMessage
{
 public:
    /** @brief Constructeur standard.
     *
     * Le constructeur initialise le nom de la source et le niveau de référence
     * de l'instance.
     *
     * @param[in] level niveau de référence de l'instance.
     * @param[in] source type de l'algorithme.
     */
  FlotMessage( const DebugLevelType& level, const string& source = "" );
  /** @brief Constructeur de copie.
   *
   * Le constructeur permet de dupliquer une instance.
   *
   * @param[in] flot instance de FlotMessage à copier.
   */
  FlotMessage( const FlotMessage& flot );
  /** @brief Destructeur, virtuel pour assurer une destruction propre des
   * classes dérivées.
   *
   * Le destructeur n'a rien de spécial à faire.
   */
  virtual ~FlotMessage();
  /** @brief Accesseur pour Stream.
   *
   * Permet d'accéder au flot de sortie.
   *
   * @return une référence sur Stream.
   */
  std::ostringstream& stream();
  /** @brief Mutateur pour NiveauCourant.
   *
   * Cette méthode permet de changer le niveau de sortie courant.
   *
   * Fait appel à operator<<( const DebugLevelType& level ).
   *
   * @param[in] level nouvelle valeur de NiveauCourant.
   */
  void setNiveauCourant( const DebugLevelType& level );
  /** @brief Accesseur pour NiveauCourant.
   *
   * Cette méthode permet d'accéder au niveau de sortie courant de l'instance.
   *
   * @return une référence constante sur NiveauCourant.
   */
  const DebugLevelType& niveauCourant() const;
  /** @brief Mutateur pour NiveauInstance.
   *
   * Cette méthode permet de changer le niveau de sortie de référence pour
   * l'instance.
   *
   * @param[in] level nouvelle valeur de NiveauInstance.
   */
  void setNiveauInstance( const DebugLevelType& level );
  /** @brief Accesseur pour NiveauInstance.
   *
   * Cette méthode permet d'accéder au niveau de sortie de référence de
   * l'instance.
   *
   * @return une référence constante sur NiveauInstance.
   */
  const DebugLevelType& niveauInstance() const;
  /** @brief Mutateur pour UtiliseCouleur.
   *
   * Cette méthode permet de modifier l'utilisation des couleurs.
   *
   * @param[in] utilCouleur nouvelle valeur pour UtiliseCouleur.
   */
  static void setUtiliseCouleur( const bool& utilCouleur );
  /** @brief Accesseur pour UtiliseCouleur.
   *
   * Cette méthode permet d'accéder au champ UtiliseCouleur.
   *
   * @return une référence constante sur UtiliseCouleur.
   */
  static const bool& utiliseCouleur();
  /** @brief Accesseur pour EstActif.
   *
   * Cette méthode permet de savoir si le flot est actif (càd si le niveau
   * courant est supérieur ou égal au niveau de référence).
   *
   * @return une référence constante sur EstActif.
   */
  const bool& estActif() const;
  /** @brief Réalise l'affichage de l'instance.
   *
   * Cette méthode affiche le contenu du champ Stream en faisant appel à
   * std::cout, suivant la valeur du manipulateur \c manip, std::endl est
   * ajouté. Le champ Stream est ensuite réinitialisé. Si le niveau courant est
   * trop bas, rien n'est affiché mais Stream est quand même réinitialisé.
   *
   * Apparemment la conversion en string peut lancer une exception, alors on a
   * une structure \c try - \c catch. Dans le cas où quelque chose ne s'est pas
   * bien passé, on envoie un message dans std::cerr.
   *
   * @param[in] manip détermine s'il faut ajouter std::endl ou non.
   *
   * @return une référence sur l'instance courante.
   */
  FlotMessage& produireSortie( const Manipulateur& manip );
  /** @brief Méthode permettant de changer le niveau courant.
   *
   * Cette méthode permet de changer à la volée le niveau courant de
   * l'instance, en utilisant :
   *
   * @code
   * FlotMessage flot( BLANK, "NomType" );
   * flot << ERR << "Une erreur" << endmsg;
   * flot << DEBUG << "Une info de debug" << endmsg;
   * @endcode
   *
   * @param[in] level niveau de sortie désiré.
   *
   * @return une référence sur l'instance courante.
   */
  FlotMessage& operator<<( const DebugLevelType& level );
  /** @brief Méthode traitant l'insertion d'un manipulateur.
   *
   * Cette méthode permet de détecter l'insertion d'un manipulateur dans le
   * flot.
   *
   * Appelle produireSortie.
   *
   * @param[in] manip instance de Manipulateur.
   *
   * @return une référence sur l'instance courante.
   */
  FlotMessage& operator<<( const Manipulateur& manip );
 protected:
  /** @brief Nom de la classe (càd le type) qui cause l'appel à la sortie.
   */
  std::string Source;
  /** @brief Flot de sortie dans lequel on stocke toute l'information voulue.
   */
  std::ostringstream Stream;
  /** @brief  Niveau de sortie actuel.
   */
  DebugLevelType NiveauCourant;
  /** @brief Niveau de sortie de l'instance.
   *
   * Ce niveau détermine si un message particulier doit être affiché ou non.
   */
  DebugLevelType NiveauInstance;
  /** @brief Permet de (dés)activer l'utilisation des couleurs.
   *
   * Détermine si les couleurs doivent être utilisées (par défaut oui) ou non
   * lors de l'affichage.
   */
  static bool UtiliseCouleur;
  /** @brief Permet de savoir si un message doit être affiché.
   *
   * Détermine si la sortie est activée (càd si le niveau demandé est supérieur
   * ou égal au niveau de référence de l'instance).
   */
  bool EstActif;

 private:
  /** @brief Décorations pour chaque niveau de sortie.
   *
   * Conteneur qui associe chaque niveau à un type d'affichage (couleur de la
   * police, du fond et attribut de police).
   */
  static const std::map< DebugLevelType, CColor > Decorations;

  /** @brief Nom de chaque niveau de sortie.
   *
   * Conteneur qui associe chaque niveau à un nom, qui est utilisé pour
   * l'affichage.
   */
  static const std::map< DebugLevelType, std::string > Textes;

  /** @brief Méthode activant la sortie.
   *
   * Cette méthode permet d'activer la sortie.
   */
  void activer();
  /** @brief Méthode désactivant la sortie.
   *
   * Cette méthode permet de désactiver la sortie.
   */
  void desactiver();
};

/** @brief Template permettant l'utilisation d'une instance de FlotMessage
 * comme un flot de sortie standard.
 *
 * Template qui permet d'afficher dans un FlotMessage n'importe quel objet pour
 * autant que l'opérateur d'insertion (@f$<<@f$) soit définit.
 *
 * @tparam T type de l'objet à afficher. Le seul prérequis est que l'opérateur
 * suivant doit exister :
 * @code
 * std::ostream& operator<<( std::ostream&, T );
 * @endcode
 *
 * @param[in] flot instance de FlotMessage dans lequel l'affichage sera
 * réalisé.
 * @param[in] arg objet que l'on veut afficher.
 *
 * @return une référence sur flot, de manière à pouvoir utiliser plusieurs
 * opérateurs d'insertion à la suite.
 */
template< typename T >
FlotMessage& operator<<( FlotMessage& flot, const T& arg )
{
  flot.stream() << arg;
  return flot;
}

/** @brief Fonction permettant d'accéder à FlotMessageGlobal;
 *
 * Cette fonction permet d'avoir accès à l'objet FlotMessageGlobal.
 *
 * @return  une référence sur FlotMessageGlobal.
 */
FlotMessage& flotMessageGlobal();

#endif // FLOTMESSAGE_H
